home *** CD-ROM | disk | FTP | other *** search
/ Languguage OS 2 / Languguage OS II Version 10-94 (Knowledge Media)(1994).ISO / gnu / bash_114.zip / bash-1.14.2 / builtins / cd.def < prev    next >
Encoding:
Text File  |  1994-04-05  |  16.5 KB  |  690 lines

  1. This file is cd.def, from which is created cd.c.  It implements the
  2. builtins "cd", "pwd", "pushd", "popd", and "dirs" in Bash.
  3.  
  4. Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
  5.  
  6. This file is part of GNU Bash, the Bourne Again SHell.
  7.  
  8. Bash is free software; you can redistribute it and/or modify it under
  9. the terms of the GNU General Public License as published by the Free
  10. Software Foundation; either version 1, or (at your option) any later
  11. version.
  12.  
  13. Bash is distributed in the hope that it will be useful, but WITHOUT ANY
  14. WARRANTY; without even the implied warranty of MERCHANTABILITY or
  15. FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  16. for more details.
  17.  
  18. You should have received a copy of the GNU General Public License along
  19. with Bash; see the file COPYING.  If not, write to the Free Software
  20. Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  21.  
  22. $PRODUCES cd.c
  23.  
  24. #include <stdio.h>
  25. #include <sys/param.h>
  26.  
  27. #if defined (HAVE_STRING_H)
  28. #  include <string.h>
  29. #else /* !HAVE_STRING_H */
  30. #  include <strings.h>
  31. #endif /* !HAVE_STRING_H */
  32.  
  33. #include <errno.h>
  34. #include <tilde/tilde.h>
  35.  
  36. #include "../shell.h"
  37. #include "../flags.h"
  38. #include "../maxpath.h"
  39. #include "common.h"
  40.  
  41. #if !defined (errno)
  42. extern int errno;
  43. #endif /* !errno */
  44.  
  45. static int change_to_directory (), cd_to_string ();
  46.  
  47. $BUILTIN cd
  48. $FUNCTION cd_builtin
  49. $SHORT_DOC cd [dir]
  50. Change the current directory to DIR.  The variable $HOME is the
  51. default DIR.  The variable $CDPATH defines the search path for
  52. the directory containing DIR.  Alternative directory names are
  53. separated by a colon (:).  A null directory name is the same as
  54. the current directory, i.e. `.'.  If DIR begins with a slash (/),
  55. then $CDPATH is not used.  If the directory is not found, and the
  56. shell variable `cdable_vars' exists, then try the word as a variable
  57. name.  If that variable has a value, then cd to the value of that
  58. variable.
  59. $END
  60.  
  61. /* This builtin is ultimately the way that all user-visible commands should
  62.    change the current working directory.  It is called by cd_to_string (),
  63.    so the programming interface is simple, and it handles errors and
  64.    restrictions properly. */
  65. int
  66. cd_builtin (list)
  67.      WORD_LIST *list;
  68. {
  69.   char *dirname;
  70.  
  71. #if defined (RESTRICTED_SHELL)
  72.   if (restricted)
  73.     {
  74.       builtin_error ("restricted");
  75.       return (EXECUTION_FAILURE);
  76.     }
  77. #endif /* RESTRICTED_SHELL */
  78.  
  79.   if (list)
  80.     {
  81.       char *extract_colon_unit ();
  82.       char *path_string = get_string_value ("CDPATH");
  83.       char *path;
  84.       int path_index = 0, dirlen, pathlen;
  85.  
  86.       dirname = list->word->word;
  87.  
  88.       if (path_string && !absolute_pathname (dirname))
  89.     {
  90.       while ((path = extract_colon_unit (path_string, &path_index)))
  91.         {
  92.           char *dir;
  93.  
  94.           if (*path == '~')
  95.         {
  96.           char *te_string = tilde_expand (path);
  97.  
  98.           free (path);
  99.           path = te_string;
  100.         }
  101.  
  102.           if (!*path)
  103.         {
  104.           free (path);
  105.           path = xmalloc (2);
  106.           path[0] = '.';    /* by definition. */
  107.           path[1] = '\0';
  108.         }
  109.  
  110.           dirlen = strlen (dirname);
  111.           pathlen = strlen (path);
  112.           dir = xmalloc (2 + dirlen + pathlen);
  113.           strcpy (dir, path);
  114.           if (path[pathlen - 1] != '/')
  115.             {
  116.               dir[pathlen++] = '/';
  117.               dir[pathlen] = '\0';
  118.             }
  119.           strcpy (dir + pathlen, dirname);
  120.           free (path);
  121.  
  122.           if (change_to_directory (dir))
  123.         {
  124.           /* replaces (strncmp (dir, "./", 2) != 0) */
  125.           if (dir[0] != '.' || dir[1] != '/')
  126.             printf ("%s\n", dir);
  127.  
  128.           free (dir);
  129.           goto bind_and_exit;
  130.         }
  131.           else
  132.         free (dir);
  133.         }
  134.     }
  135.  
  136.       if (!change_to_directory (dirname))
  137.     {
  138.       /* Maybe this is `cd -', equivalent to `cd $OLDPWD' */
  139.       if (dirname[0] == '-' && dirname[1] == '\0')
  140.         {
  141.           char *t = get_string_value ("OLDPWD");
  142.  
  143.           if (t && change_to_directory (t))
  144.         goto bind_and_exit;
  145.         }
  146.  
  147.       /* If the user requests it, then perhaps this is the name of
  148.          a shell variable, whose value contains the directory to
  149.          change to.  If that is the case, then change to that
  150.          directory. */
  151.       if (find_variable ("cdable_vars"))
  152.         {
  153.           char *t = get_string_value (dirname);
  154.  
  155.           if (t && change_to_directory (t))
  156.         {
  157.           printf ("%s\n", t);
  158.           goto bind_and_exit;
  159.         }
  160.         }
  161.  
  162.       file_error (dirname);
  163.       return (EXECUTION_FAILURE);
  164.     }
  165.       goto bind_and_exit;
  166.     }
  167.   else
  168.     {
  169.       dirname = get_string_value ("HOME");
  170.  
  171.       if (!dirname)
  172.     return (EXECUTION_FAILURE);
  173.  
  174.       if (!change_to_directory (dirname))
  175.     {
  176.       file_error (dirname);
  177.       return (EXECUTION_FAILURE);
  178.     }
  179.  
  180.     bind_and_exit:
  181.       {
  182.     char *directory;
  183.  
  184.     directory = get_working_directory ("cd");
  185.  
  186.     bind_variable ("OLDPWD", get_string_value ("PWD"));
  187.     bind_variable ("PWD", directory);
  188.  
  189.     FREE (directory);
  190.       }
  191.       return (EXECUTION_SUCCESS);
  192.     }
  193. }
  194.  
  195. $BUILTIN pwd
  196. $FUNCTION pwd_builtin
  197. $SHORT_DOC pwd
  198. Print the current working directory.
  199. $END
  200.  
  201. /* Non-zero means that pwd always give verbatim directory, regardless of
  202.    symbolic link following. */
  203. static int verbatim_pwd;
  204.  
  205. /* Print the name of the current working directory. */
  206. pwd_builtin (list)
  207.      WORD_LIST *list;
  208. {
  209.   char *directory, *s;
  210.  
  211. #if 0
  212.   no_args (list);
  213. #else
  214.   verbatim_pwd = no_symbolic_links;
  215.   if (list && (s = list->word->word) && s[0] == '-' && s[1] == 'P' && !s[2])
  216.     verbatim_pwd = 1;
  217. #endif
  218.  
  219.   if (verbatim_pwd)
  220.     {
  221.       char *buffer = xmalloc (MAXPATHLEN);
  222.       directory = getwd (buffer);
  223.  
  224.       if (!directory)
  225.     {
  226.       builtin_error ("%s", buffer);
  227.       free (buffer);
  228.     }
  229.     }
  230.   else
  231.     directory = get_working_directory ("pwd");
  232.  
  233.   if (directory)
  234.     {
  235.       printf ("%s\n", directory);
  236.       fflush (stdout);
  237.       free (directory);
  238.       return (EXECUTION_SUCCESS);
  239.     }
  240.   else
  241.     return (EXECUTION_FAILURE);
  242. }
  243.  
  244. $BUILTIN pushd
  245. $FUNCTION pushd_builtin
  246. $DEPENDS_ON PUSHD_AND_POPD
  247. $SHORT_DOC pushd [dir | +n | -n]
  248. Adds a directory to the top of the directory stack, or rotates
  249. the stack, making the new top of the stack the current working
  250. directory.  With no arguments, exchanges the top two directories.
  251.  
  252. +n    Rotates the stack so that the Nth directory (counting
  253.     from the left of the list shown by `dirs') is at the top.
  254.  
  255. -n    Rotates the stack so that the Nth directory (counting
  256.     from the right) is at the top.
  257.  
  258. dir    adds DIR to the directory stack at the top, making it the
  259.     new current working directory.
  260.  
  261. You can see the directory stack with the `dirs' command.
  262. $END
  263.  
  264. #if defined (PUSHD_AND_POPD)
  265. /* Some useful commands whose behaviour has been observed in Csh. */
  266.  
  267. /* The list of remembered directories. */
  268. static char **pushd_directory_list = (char **)NULL;
  269.  
  270. /* Number of existing slots in this list. */
  271. static int directory_list_size = 0;
  272.  
  273. /* Offset to the end of the list. */
  274. static int directory_list_offset = 0;
  275.  
  276. pushd_builtin (list)
  277.      WORD_LIST *list;
  278. {
  279.   char *temp, *current_directory;
  280.   int j = directory_list_offset - 1;
  281.   char direction = '+';
  282.  
  283.   /* If there is no argument list then switch current and
  284.      top of list. */
  285.   if (!list)
  286.     {
  287.       if (!directory_list_offset)
  288.     {
  289.       builtin_error ("No other directory");
  290.       return (EXECUTION_FAILURE);
  291.     }
  292.  
  293.       current_directory = get_working_directory ("pushd");
  294.       if (!current_directory)
  295.     return (EXECUTION_FAILURE);
  296.  
  297.       temp = pushd_directory_list[j];
  298.       pushd_directory_list[j] = current_directory;
  299.       goto change_to_temp;
  300.     }
  301.   else
  302.     {
  303.       direction = *(list->word->word);
  304.       if (direction == '+' || direction == '-')
  305.     {
  306.       int num;
  307.       if (1 == sscanf (&(list->word->word)[1], "%d", &num))
  308.         {
  309.           if (direction == '-')
  310.         num = directory_list_offset - num;
  311.  
  312.           if (num > directory_list_offset || num < 0)
  313.         {
  314.           if (!directory_list_offset)
  315.             builtin_error ("Directory stack empty");
  316.           else
  317.             builtin_error ("Stack contains only %d directories",
  318.                     directory_list_offset + 1);
  319.           return (EXECUTION_FAILURE);
  320.         }
  321.           else
  322.         {
  323.           /* Rotate the stack num times.  Remember, the
  324.              current directory acts like it is part of the
  325.              stack. */
  326.           temp = get_working_directory ("pushd");
  327.  
  328.           if (!num)
  329.             goto change_to_temp;
  330.  
  331.           do
  332.             {
  333.               char *top =
  334.             pushd_directory_list[directory_list_offset - 1];
  335.  
  336.               for (j = directory_list_offset - 2; j > -1; j--)
  337.             pushd_directory_list[j + 1] = pushd_directory_list[j];
  338.  
  339.               pushd_directory_list[j + 1] = temp;
  340.  
  341.               temp = top;
  342.               num--;
  343.             }
  344.           while (num);
  345.  
  346.           temp = savestring (temp);
  347.         change_to_temp:
  348.           {
  349.             int tt = EXECUTION_FAILURE;
  350.  
  351.             if (temp)
  352.               {
  353.             tt = cd_to_string (temp);
  354.             free (temp);
  355.               }
  356.  
  357.             if ((tt == EXECUTION_SUCCESS))
  358.               dirs_builtin ((WORD_LIST *)NULL);
  359.  
  360.             return (tt);
  361.           }
  362.         }
  363.         }
  364.     }
  365.  
  366.       /* Change to the directory in list->word->word.  Save the current
  367.      directory on the top of the stack. */
  368.       current_directory = get_working_directory ("pushd");
  369.       if (!current_directory)
  370.     return (EXECUTION_FAILURE);
  371.  
  372.       if (cd_builtin (list) == EXECUTION_SUCCESS)
  373.     {
  374.       if (directory_list_offset == directory_list_size)
  375.         {
  376.           pushd_directory_list = (char **)
  377.         xrealloc (pushd_directory_list,
  378.               (directory_list_size += 10) * sizeof (char *));
  379.         }
  380.       pushd_directory_list[directory_list_offset++] = current_directory;
  381.  
  382.       dirs_builtin ((WORD_LIST *)NULL);
  383.  
  384.       return (EXECUTION_SUCCESS);
  385.     }
  386.       else
  387.     {
  388.       free (current_directory);
  389.       return (EXECUTION_FAILURE);
  390.     }
  391.     }
  392. }
  393. #endif /* PUSHD_AND_POPD */
  394.  
  395. $BUILTIN dirs
  396. $FUNCTION dirs_builtin
  397. $DEPENDS_ON PUSHD_AND_POPD
  398. $SHORT_DOC dirs [-l]
  399. Display the list of currently remembered directories.  Directories
  400. find their way onto the list with the `pushd' command; you can get
  401. back up through the list with the `popd' command.
  402.  
  403. The -l flag specifies that `dirs' should not print shorthand versions
  404. of directories which are relative to your home directory.  This means
  405. that `~/bin' might be displayed as `/homes/bfox/bin'.
  406. $END
  407.  
  408. #if defined (PUSHD_AND_POPD)
  409. /* Print the current list of directories on the directory stack. */
  410. dirs_builtin (list)
  411.      WORD_LIST *list;
  412. {
  413.   int i, format, desired_index, index_flag;
  414.   char *temp, *w;
  415.  
  416.   format = index_flag = 0;
  417.   desired_index = -1;
  418.   /* Maybe do long form or print specific dir stack entry? */
  419.   while (list)
  420.     {
  421.       if (strcmp (list->word->word, "-l") == 0)
  422.     {
  423.       format++;
  424.       list = list->next;
  425.     }
  426.       else if (*list->word->word == '+' && all_digits (list->word->word + 1))
  427.     {
  428.       w = list->word->word + 1;
  429.       index_flag = 1;
  430.       i = atoi (w);
  431.       /* dirs +0 prints the current working directory. */
  432.       if (i == 0)
  433.         desired_index = i;
  434.       else if (i == directory_list_offset)
  435.         {
  436.           desired_index = 0;
  437.           index_flag = 2;
  438.         }
  439.       else
  440.         desired_index = directory_list_offset - i;
  441.       list = list->next;
  442.     }
  443.       else if (*list->word->word == '-' && all_digits (list->word->word + 1))
  444.     {
  445.       w = list->word->word + 1;
  446.       i = atoi (w);
  447.       index_flag = 2;
  448.       /* dirs -X where X is directory_list_offset prints the current
  449.          working directory. */
  450.       if (i == directory_list_offset)
  451.         {
  452.           index_flag = 1;
  453.           desired_index = 0;
  454.         }
  455.       else
  456.         desired_index = i;
  457.       list = list->next;
  458.     }
  459.       else
  460.     {
  461.       bad_option (list->word->word);
  462.       return (EXECUTION_FAILURE);
  463.     }
  464.     }
  465.  
  466.   if (index_flag && (desired_index < 0 || desired_index > directory_list_offset))
  467.     {
  468.       if (directory_list_offset == 0)
  469.     builtin_error ("directory stack empty");
  470.       else
  471.     builtin_error ("%s: bad directory stack index", w);
  472.       return (EXECUTION_FAILURE);
  473.     }
  474.  
  475.   /* The first directory printed is always the current working directory. */
  476.   if (!index_flag || (index_flag == 1 && desired_index == 0))
  477.     {
  478.       temp = get_working_directory ("dirs");
  479.       if (!temp)
  480.     temp = savestring ("<no directory>");
  481.       printf ("%s", format ? temp : polite_directory_format (temp));
  482.       free (temp);
  483.       if (index_flag)
  484.     {
  485.       putchar ('\n');
  486.       return EXECUTION_SUCCESS;
  487.     }
  488.     }
  489.  
  490. #define DIRSTACK_ENTRY(i) \
  491.     format ? pushd_directory_list[i] \
  492.            : polite_directory_format (pushd_directory_list[i])
  493.  
  494.   /* Now print the requested directory stack entries. */
  495.   if (index_flag)
  496.     printf ("%s", DIRSTACK_ENTRY (desired_index));
  497.   else
  498.     for (i = (directory_list_offset - 1); i > -1; i--)
  499.       printf (" %s", DIRSTACK_ENTRY (i));
  500.  
  501.   putchar ('\n');
  502.   fflush (stdout);
  503.   return (EXECUTION_SUCCESS);
  504. }
  505. #endif /* PUSHD_AND_POPD */
  506.  
  507. $BUILTIN popd
  508. $FUNCTION popd_builtin
  509. $DEPENDS_ON PUSHD_AND_POPD
  510. $SHORT_DOC popd [+n | -n]
  511. Removes entries from the directory stack.  With no arguments,
  512. removes the top directory from the stack, and cd's to the new
  513. top directory.
  514.  
  515. +n    removes the Nth entry counting from the left of the list
  516.     shown by `dirs', starting with zero.  For example: `popd +0'
  517.     removes the first directory, `popd +1' the second.
  518.  
  519. -n    removes the Nth entry counting from the right of the list
  520.     shown by `dirs', starting with zero.  For example: `popd -0'
  521.     removes the last directory, `popd -1' the next to last.
  522.  
  523. You can see the directory stack with the `dirs' command.
  524. $END
  525.  
  526. #if defined (PUSHD_AND_POPD)
  527. /* Pop the directory stack, and then change to the new top of the stack.
  528.    If LIST is non-null it should consist of a word +N or -N, which says
  529.    what element to delete from the stack.  The default is the top one. */
  530. popd_builtin (list)
  531.      WORD_LIST *list;
  532. {
  533.   register int i;
  534.   int which = 0;
  535.   char direction = '+';
  536.  
  537.   if (list)
  538.     {
  539.       direction = *(list->word->word);
  540.  
  541.       if ((direction != '+' && direction != '-') ||
  542.       (1 != sscanf (&((list->word->word)[1]), "%d", &which)))
  543.     {
  544.       builtin_error ("bad arg `%s'", list->word->word);
  545.       return (EXECUTION_FAILURE);
  546.     }
  547.     }
  548.  
  549.   if (which > directory_list_offset || (!directory_list_offset && !which))
  550.     {
  551.       if (!directory_list_offset)
  552.     builtin_error ("Directory stack empty");
  553.       else
  554.     builtin_error ("Stack contains only %d directories",
  555.             directory_list_offset + 1);
  556.       return (EXECUTION_FAILURE);
  557.     }
  558.  
  559.   /* Handle case of no specification, or top of stack specification. */
  560.   if ((direction == '+' && which == 0) ||
  561.       (direction == '-' && which == directory_list_offset))
  562.     {
  563.       i = cd_to_string (pushd_directory_list[directory_list_offset - 1]);
  564.       if (i != EXECUTION_SUCCESS)
  565.     return (i);
  566.       free (pushd_directory_list[--directory_list_offset]);
  567.     }
  568.   else
  569.     {
  570.       /* Since an offset other than the top directory was specified,
  571.      remove that directory from the list and shift the remainder
  572.      of the list into place. */
  573.  
  574.       if (direction == '+')
  575.     i = directory_list_offset - which;
  576.       else
  577.     i = which;
  578.  
  579.       free (pushd_directory_list[i]);
  580.       directory_list_offset--;
  581.  
  582.       /* Shift the remainder of the list into place. */
  583.       for (; i < directory_list_offset; i++)
  584.     pushd_directory_list[i] = pushd_directory_list[i + 1];
  585.     }
  586.  
  587.   dirs_builtin ((WORD_LIST *)NULL);
  588.  
  589.   return (EXECUTION_SUCCESS);
  590. }
  591. #endif /* PUSHD_AND_POPD */
  592.  
  593. /* Do the work of changing to the directory NEWDIR.  Handle symbolic
  594.    link following, etc. */
  595.  
  596. static int
  597. change_to_directory (newdir)
  598.      char *newdir;
  599. {
  600.   char *t;
  601.  
  602.   if (!no_symbolic_links)
  603.     {
  604.       int chdir_return = 0;
  605.       char *tdir = (char *)NULL;
  606.  
  607.       if (!the_current_working_directory)
  608.     {
  609.       t = get_working_directory ("cd_links");
  610.       FREE (t);
  611.     }
  612.  
  613.       if (the_current_working_directory)
  614.     t = make_absolute (newdir, the_current_working_directory);
  615.       else
  616.     t = savestring (newdir);
  617.  
  618.       /* TDIR is the canonicalized absolute pathname of the NEWDIR. */
  619.       tdir = canonicalize_pathname (t);
  620.  
  621.       /* Use the canonicalized version of NEWDIR, or, if canonicalization
  622.      failed, use the non-canonical form. */
  623.       if (tdir && *tdir)
  624.     free (t);
  625.       else
  626.     {
  627.       FREE (tdir);
  628.  
  629.       tdir = t;
  630.     }
  631.  
  632.       if (chdir (tdir) < 0)
  633.     {
  634.       int err;
  635.  
  636.       chdir_return = 0;
  637.       free (tdir);
  638.  
  639.       err = errno;
  640.  
  641.       /* We failed changing to the canonicalized directory name.  Try
  642.          what the user passed verbatim.  If we succeed, reinitialize
  643.          the_current_working_directory. */
  644.       if (chdir (newdir) == 0)
  645.         {
  646.           chdir_return = 1;
  647.           if (the_current_working_directory)
  648.         {
  649.           free (the_current_working_directory);
  650.           the_current_working_directory = (char *)NULL;
  651.         }
  652.  
  653.           tdir = get_working_directory ("cd");
  654.           FREE (tdir);
  655.         }
  656.       else
  657.         errno = err;
  658.     }
  659.       else
  660.     {
  661.       chdir_return = 1;
  662.  
  663.       FREE (the_current_working_directory);
  664.       the_current_working_directory = tdir;
  665.     }
  666.  
  667.       return (chdir_return);
  668.     }
  669.   else
  670.     {
  671.       if (chdir (newdir) < 0)
  672.     return (0);
  673.       else
  674.     return (1);
  675.     }
  676. }
  677.  
  678. /* Switch to the directory in NAME.  This uses the cd_builtin to do the work,
  679.    so if the result is EXECUTION_FAILURE then an error message has already
  680.    been printed. */
  681. static int
  682. cd_to_string (name)
  683.      char *name;
  684. {
  685.   WORD_LIST *tlist = make_word_list (make_word (name), NULL);
  686.   int result = (cd_builtin (tlist));
  687.   dispose_words (tlist);
  688.   return (result);
  689. }
  690.